/*
* Author:  Greg Nowak and Vaibhav Sharma
* Michigan State University
* 
* This is the implementation of the NFC Wireless Sensor Network Application
*/
#include "Timer.h"  /* For timer control */
#include "printf.h"
#include "NFCWireless.h"
#include "Msp430Adc12.h"

/* Data logging variables */
	typedef nx_struct flashLogEntry_t 
	{
    	nx_uint32_t timeStamp;
    	nx_uint16_t temperature;
    	nx_uint8_t nodeID;
    	nx_uint8_t sentStatus;
  	} flashLogEntry_t;

module NFCWirelessC
{
  	uses interface Timer<TMilli> as Timer0;  /* Reads temperature, logs to flash */
  	uses interface Timer<TMilli> as Timer1;  /* Sends all data in flash */
	uses interface Timer<TMilli> as TimerDiss; /* Allows dissemination some time to work, keeping the radio on during this time */
	uses interface Timer<TMilli> as TimerToSleep; /* Allows dissemination some time to work, keeping the radio on during this time */
  	uses interface LocalTime<TMilli>;
 	uses interface Leds;
  	uses interface Boot;
	uses interface LogRead;
	uses interface LogWrite;
	uses interface Read<uint16_t> as TemperatureReading;
	uses interface Read<uint16_t> as BatteryVoltage;
    //uses interface Packet;
    //uses interface AMPacket;
    //uses interface AMSend;
    //uses interface Receive;
    uses interface SplitControl as RadioControl;
    //uses interface HplMsp430GeneralIO as EnergyHarvesterDiscrete;
    
    uses interface StdControl as DisseminationControl;
	uses interface DisseminationValue<flashLogEntry_t> as Value;
    uses interface DisseminationUpdate<flashLogEntry_t> as Update;
	

  provides interface AdcConfigure<const msp430adc12_channel_config_t*> as ACD_A4_Configure;
}
implementation
{
  /***  Module Variables Start ************************************/
  bool mModuleSleeping = FALSE;
  /* Packet Writing Variables */
  uint16_t packetCountWritten = 0;
  uint16_t packetCountRead = 0;
  uint16_t packetSentCount = 0;
  
  
  /* Timer Variables */
  uint32_t mStart_time;
  uint32_t mStop_time;
  uint32_t mTemperatureTimeStamp;
  uint32_t mDesiredSleepSchedule = SHORT_SLEEP_SCHEDULE_MILI; /* Default to short sleep cycle */
  
  bool mLogBusy = FALSE;  	
  flashLogEntry_t mLocalEntry;
  uint16_t MAX_NUM_LOG_ENTRIES = 500;  /* TODO : Just for experimentation, not actual!*/
  
  /*  Temperature sensing variables */
  uint16_t mTemperatureReading = 0;
  bool mReadBusy = FALSE;
  
  /*  Packet sending variables */
  flashLogEntry_t mLocalLogEntry;
  bool mSendBusy = FALSE;
  uint32_t mSendPacketsStartTime;
  uint32_t mSendPacketsEndTime;
  bool radioBusy = FALSE;
  message_t pkt;
  void WakeUp();
  void GoToSleep();
  /* Used to print a float via printf */
  void printfFloat(float toBePrinted) {
     uint32_t fi, f0, f1, f2;
     char c;
     float f = toBePrinted;

     if (f<0){
       c = '-'; f = -f;
     } else {
       c = ' ';
     }

     // integer portion.
     fi = (uint32_t) f;

     // decimal portion...get index for up to 3 decimal places.
     f = f - ((float) fi);
     f0 = f*10;   f0 %= 10;
     f1 = f*100;  f1 %= 10;
     f2 = f*1000; f2 %= 10;
     printf("%c%ld.%d%d%d", c, fi, (uint8_t) f0, (uint8_t) f1,  
		(uint8_t) f2);
   }
   
   uint16_t mLastVoltageRead;
   task void determineDesiredSleepSchedule()
   {
   
   		/* We are in the largest battery voltage, so we want to set the sleep schedule
   		 * to be the shortest duration (i.e. sleep the least since we have the battery anyway 
   		 */
   		if( mLastVoltageRead > (MAX_BATTERY_VOLTAGE - LARGE_SLEEP_SCHEDULE_WIDTH))
   		{
   			mDesiredSleepSchedule = SHORT_SLEEP_SCHEDULE_MILI;
		}else if( mLastVoltageRead > (MAX_BATTERY_VOLTAGE - LARGE_SLEEP_SCHEDULE_WIDTH - SMALL_SLEEP_SCHEDULE_WIDTH))
		{
			mDesiredSleepSchedule = MEDIUM_SLEEP_SCHEDULE_MILI;
		}else{
			mDesiredSleepSchedule = LONG_SLEEP_SCHEDULE_MILI;
		}
   	
   }
  	/**
	 * When the ADC Value is done reading, print the result
	 */
	event void BatteryVoltage.readDone(error_t result, uint16_t val){
		
		if ( result == SUCCESS)
		{
			/* Print the value */
			float value = ((float)val * ADC_REFERENCE_VOLTAGE)/MAX_ADC_COUNT;
			/*printf("ADC Value: ");
			printfFloat(value);
			printf(" V\n");
			printfflush();*/
			
			mLastVoltageRead = val;
			post determineDesiredSleepSchedule();
		}
	}
	
	/* Configuration necessary to do ADC convertion */
	  const msp430adc12_channel_config_t config = {
	      inch: INPUT_CHANNEL_A3,
	      sref: REFERENCE_VREFplus_AVss,
	      ref2_5v: REFVOLT_LEVEL_1_5,
	      adc12ssel: SHT_SOURCE_ACLK,
	      adc12div: SHT_CLOCK_DIV_1,
	      sht: SAMPLE_HOLD_4_CYCLES,
	      sampcon_ssel: SAMPCON_SOURCE_SMCLK,
	      sampcon_id: SAMPCON_CLOCK_DIV_1
	  };
	  /* Command needed to provide the configuration file */
	  async command const msp430adc12_channel_config_t* ACD_A4_Configure.getConfiguration()
	  {
	    return &config;
	  }
  /*******************************************************/
  /* This is the code that is called when the system starts.*/
  event void Boot.booted()
  {
//  	mModuleSleeping = TRUE;
	//call EnergyHarvesterDiscrete.makeOutput();
    call Timer0.startPeriodic( TIMER0_PERIOD);
    call Timer1.startPeriodic(TIMER1_PERIOD);
    //call Timer1.startPeriodic(mDesiredSleepSchedule+DISS_PERIOD);
    //Clear the log on start up for DEBUG only
    if (call LogWrite.erase() != SUCCESS) {
		printf("Boot.booted(): LogWrite.erase Failed\n");
		printfflush();
    }
    mLocalEntry.nodeID = TOS_NODE_ID;
  }
  
  
  /** 
   * The primary purpose of Timer 0 is to kick off a temperature read and log storage
   */
  event void Timer0.fired()
  {
  	if( !mSendBusy )
  	{
  		printf("Timer0 Fired, logging temperature\n");
        printfflush();
	  	call Leds.led1Toggle();
	  
	   	if(!mLogBusy)
	  	{
	  		mLogBusy = TRUE;  		
		  	
			/* Take a temperature reading */
			if( call TemperatureReading.read() != SUCCESS)
			{
				printf("Timer0.fired(): Reading temperature failed\n");    
				printfflush();		
			}else{
				printf("Timer0.fired():Reading temperature succeeded\n");
				printfflush();
			}        
		}     	
	}
  }
  
  /***************************************************/
  /*  Flash Logger */
	
    event void LogRead.readDone(void* buf, storage_len_t len, error_t err) 
    {
		printf("LogRead.readDone\n");
		printfflush();
    	//Check to see if the packet read is the right size
        if ( (len == sizeof(flashLogEntry_t)) )
        //if ( (len == sizeof(flashLogEntry_t)) && (buf == &mLocalLogEntry) )
        {
        	//We've read the log correctly, now send the packet
			printf("LogRead.readDone:Trying to send a packet\n");
			printfflush();
			mLogBusy = FALSE;
			//If the radio isn't busy, send all entries in the log
			if( !radioBusy)
	    	{    		
				//flashLogEntry_t* lPacket = (flashLogEntry_t*)call Packet.getPayload(&pkt, sizeof (flashLogEntry_t));						    		
				//*lPacket = mLocalLogEntry;
	            
                radioBusy = TRUE; 
                call Update.change(&mLocalLogEntry);
                printf("NodeID = %d sending update with new value = %d\n", mLocalLogEntry.nodeID, mLocalLogEntry.temperature);
		        printfflush();
                radioBusy = FALSE;
                mSendBusy = FALSE;
                if (call LogWrite.erase() != SUCCESS) {
		            printf("LogRead.readDone(): LogWrite.erase Failed\n");
		            printfflush();
                }
			
                /* if (call AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(flashLogEntry_t)) == SUCCESS) { 
				  radioBusy = TRUE;
				  //printf("LogRead.readDone:AMSending succeeded.  Radio is busy\n");
				  //printfflush();
				}else{
					radioBusy = FALSE;
					//printf("LogRead.readDone:AMSend failed\n");
					//printfflush();
				}*/
				
			}else{
				//printf("LogRead.readDone: Radio is busy\n");
				//printfflush();
			}
    	}else{
    		printf("LogRead.readDone: buf did not match\n");
    		printfflush();
			mSendBusy = FALSE;
		}
        call TimerToSleep.startOneShot(TIMER_TO_SLEEP_PERIOD);   	
        //GoToSleep();
	}
	
	event void LogWrite.eraseDone(error_t err) 
	{
	}
	event void LogWrite.appendDone(void* buf, storage_len_t len, 
                                 bool recordsLost, error_t err) 
    {
		mLogBusy = FALSE;	
		//printf("LogWrite.appendDone():Logging complete\n");
		//printfflush();
	}

 	event void LogRead.seekDone(error_t err) 
 	{
 	}

  	event void LogWrite.syncDone(error_t err) 
  	{
  	}
	
  /**************************************************/
  
  /***** Sensor Reading  ***********************/
  /**
   * After reading the sensor, we want to store this information to the log
   */
  event void TemperatureReading.readDone(error_t result, uint16_t data) {
  	//printf("Read.readDone(): mLogBusy=%d, packetCount=%d, MAX_NUM_ENTRIES=%d\n", mLogBusy, packetCountWritten, MAX_NUM_LOG_ENTRIES);
  	//printfflush();
  	//We only store a certain amount of data, so once we've reached the limit, stop logging
	if(packetCountWritten < MAX_NUM_LOG_ENTRIES)
	{
	    if (result != SUCCESS)
		{
	  		/* Set the data to garbage because it wasn't right anyway */
	  		data = 0xffff;
		}	  	
		
		mLocalEntry.timeStamp = call Timer0.getNow();
		mLocalEntry.temperature = data;   	
		mLocalEntry.nodeID = TOS_NODE_ID;
		//mLocalEntry.nodeID = TOS_NODE_ID;
		mLocalEntry.sentStatus = 0;
	
		/* Write data to log */
		if (call LogWrite.append(&mLocalEntry, sizeof(flashLogEntry_t)) != SUCCESS) {
			mLogBusy = FALSE;
			//printf("Read.readDone():Packet write failed\n");
			//printfflush();
		}else{
			packetCountWritten++;  //Increase the number of packets written
			//printf("Read.readDone():Packet Written Count: %d\n", packetCountWritten);
			//printfflush();
		}
	}
  }		
  
  event void Value.changed() {
		const flashLogEntry_t *new_value = call Value.get();
        printf("NodeID = %d seeing node with ID = %d with value changed = %d\n", mLocalEntry.nodeID, (new_value)->nodeID, (new_value)->temperature);
		printfflush();
	}
 
  /****** Sensor Reading End ************************/
  /****** RadioControl Start *********************************/
  
  /**
   * To go to sleep, turn off RadioControl
   */
  void GoToSleep()
  {
    printf("Node id = %d going to sleep\n",mLocalLogEntry.nodeID);
    printfflush();
  	mModuleSleeping = TRUE;
  	//call EnergyHarvesterDiscrete.clr();
  	call RadioControl.stop();
    printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
  }
  
  /**
   * When we wake up, turn on RadioControl
   */
  void WakeUp()
  {
    printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
    printf("Node id = %d waking up\n",mLocalEntry.nodeID);
    printfflush();
  	mModuleSleeping = FALSE;
  	//call EnergyHarvesterDiscrete.set();
  	call RadioControl.start();	
  }
  
  /**
  * This is in control of sending all the stored log files 
  */
  event void Timer1.fired() {  	
 	static uint32_t Timer1CurrentPeriod = SHORT_SLEEP_SCHEDULE_MILI;
 	/* If the last time we called voltage read prompted us to 
 	 * change the sleep schedule, do so now
 	 */
 	/*if( Timer1CurrentPeriod != mDesiredSleepSchedule)
 	{
 		call Timer1.startPeriodic(mDesiredSleepSchedule);
 	}*/
	/* Read the battery voltage so the next time we come here we can determine
	 * if we need to change the time of timer1 firing
	 */
	call BatteryVoltage.read();
	/* Turn the radio back on */
    WakeUp();
    
  	call Leds.led2Toggle();
  	if( packetSentCount == 0)
  	{
  		mSendPacketsStartTime = call LocalTime.get();
  	}
  }

  event void RadioControl.startDone(error_t err) {
    printf("RadioControl Start Done\n");
    printfflush();
    if (err == SUCCESS) {
        call DisseminationControl.start();
        printf("Node with id = %d started Radio\n",mLocalEntry.nodeID); 
        printfflush();
		/* TimerDiss is being used to keep the radio on for a specific amount of time 
           and also to let DisseminationControl.start() complete*/
        call TimerDiss.startOneShot(DISS_PERIOD);
    }
    else {
      printf("Node with id = %d failed to start Radio, restarting Radio\n",mLocalEntry.nodeID); 
      printfflush();
      call RadioControl.start();      
    }
  }
  
  event void TimerToSleep.fired() {
      GoToSleep();
  }
 
  event void TimerDiss.fired() {
  /* Code for disseminating node */
    mSendBusy = TRUE;
	printf("TimerDiss fired\n");
	printfflush();
    if (call LogRead.read(&mLocalLogEntry, sizeof(flashLogEntry_t)) != SUCCESS) 
    {
    	mSendBusy = FALSE;
		// Handle error.
		printf("TimerDiss.fired() LogRead Error\n");
		printfflush();
		
	}else{
		//printf("Timer1.fired() Load Read success\n");
		//printfflush();
	}
    /* End of code for disseminating node */
  }

  event void RadioControl.stopDone(error_t err) {
  }
	
 /* Commenting this part out because we dont want to be using AMSend anymore */
/* 
  event void AMSend.sendDone(message_t* msg, error_t error) {
    if (&pkt == msg && error == SUCCESS) {
      	radioBusy = FALSE;
      	packetSentCount++;
		//printf("AMSend.sendDone(): Packet # %d Sent\n", packetSentCount);
    	//printfflush();
      	
		if( packetSentCount == packetCountWritten)
		{			
			mSendPacketsEndTime = call LocalTime.get();
			printf("AMSend.sendDone(): Time to send %d log entries: %u\n",packetSentCount, (mSendPacketsEndTime - mSendPacketsStartTime));
			printfflush();
			mSendPacketsEndTime = 0;
			mSendPacketsStartTime = 0;
			packetSentCount = 0;
			packetCountWritten = 0;		
			call Leds.led2Off();				
			mSendBusy = FALSE;
			GoToSleep();
		}else{
			mLogBusy = FALSE;
			//If there are more packets to send, do another log read to initiate another send
		    if (call LogRead.read(&mLocalLogEntry, sizeof(flashLogEntry_t)) != SUCCESS) 
		    {
				// Handle error.
				//printf("AMSend.sendDone(): Load Read Error\n");
				//printfflush();
			}else{
				//printf("AMSend.sendDone(): Load Read success\n");
				//printfflush();
			}
		}
    }else{
    	
	}

    
  }

  event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len) {
  	      printf("Received Packet\n");
      printfflush();
    if (len == sizeof(flashLogEntry_t)) {
	  flashLogEntry_t* btrpkt = (flashLogEntry_t*)payload;
      printf("Received Packet Timestamp: %d   Temperature: %d\n", btrpkt->timeStamp, btrpkt->temperature);
      printfflush();
	}
	return msg;
  }

*/
/* End - Commenting this part out because we dont want to be using AMSend anymore */
}
